/**
 * @file HttpUtil.java
 * @brief HttpUtil is a single class containing methods to conveniently perform HTTP
 * requests. HttpUtil only uses regular java io and net functionality and does
 * not depend on external libraries.
 * The class contains methods to perform a get, post, put, and delete request,
 * and supports posting forms. Optionally, one can provide headers.
 * <p>
 * Example usage:
 * <p>
 * // get
 * String res = HttpUtil.get("http://www.google.com");
 * <p>
 * // post
 * String res = HttpUtil.post("http://sendmedata.com", "This is the data");
 * <p>
 * // post form
 * Map<String, String> params = new HashMap<String, String>();
 * params.put("firstname", "Joe");
 * params.put("lastname", "Smith");
 * params.put("age", "28");
 * String res = HttpUtil.postForm("http://site.com/newuser", params);
 * <p>
 * // append query parameters to url
 * String url = "http://mydatabase.com/users";
 * Map<String, String> params = new HashMap<String, String>();
 * params.put("orderby", "name");
 * params.put("limit", "10");
 * String fullUrl = HttpUtil.appendQueryParams(url, params);
 * // fullUrl = "http://mydatabase.com/user?orderby=name&limit=10"
 * @license Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 * <p>
 * Copyright (c) 2012 Almende B.V.
 * @author Jos de Jong, <jos@almende.org>
 * @date 2012-05-14
 */

package common.util.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.rlyy.aibase.utils.base.ThinkException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class HttpUtil {

    private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);

    /**
     * Send a get request
     *
     * @param url
     * @return response
     */
    static public String get(String url) throws IOException {
        return get(url, null);
    }

    /**
     * Send a get request
     *
     * @param url     Url as string
     * @param headers Optional map with headers
     * @return response   Response as string
     */
    static public String get(String url,
                             Map<String, String> headers) throws IOException {
        return fetch("GET", url, null, headers);
    }

    public static void main(String[] args) throws IOException {
//		System.out.println(get("https://www.baidu.com"));

        Map<String, String> _data_ = new HashMap<>();
        _data_.put("User_Id", "1234546489");

        Map<String, Object> param1 = new HashMap<>();
        param1.put("$api", "rsj_pub_file_token");
        param1.put("$tid", "TID_3A90E1D6152242D3B6D5B137018430DE");
        param1.put("$appId", "rsj");
        param1.put("_data_", _data_);

        Map<String, Object> param = new HashMap<>();
        param.put("$json", param1);

        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", "application/json");
        headers.put("Content-Type", "application/json");

        String post = post("http://10.254.64.10:9095/file/getFileAccessToken"
                , JSON.toJSONString(param), headers);

        JSONObject jsonObject = JSON.parseObject(post);
        String token = String.valueOf(jsonObject.getObject("_data_", JSONObject.class).get("File_Token"));

        System.out.println(JSON.toJSONString(post));
    }


    public static Map<String, String> createJSONHeader() {
        Map<String, String> headers = new HashMap<>();
        headers.put("Accept", "application/json");
        headers.put("Content-Type", "application/json");
//		headers.put(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE);
        return headers;
    }

    /**
     * Send a post request
     *
     * @param url     Url as string
     * @param body    Request body as string
     * @param headers Optional map with headers
     * @return response   Response as string
     */
    static public String post(String url, String body,
                              Map<String, String> headers) throws IOException {
        return fetch("POST", url, body, headers);
    }

    /**
     * Send a post request
     *
     * @param url  Url as string
     * @param body Request body as string
     * @return response   Response as string
     */
    static public String post(String url, String body) throws IOException {
        return post(url, body, null);
    }

    /**
     * Post a form with parameters
     *
     * @param url    Url as string
     * @param params map with parameters/values
     * @return response   Response as string
     */
    static public String postForm(String url, Map<String, String> params)
            throws IOException {
        return postForm(url, params, null);
    }

    /**
     * Post a form with parameters
     *
     * @param url     Url as string
     * @param params  Map with parameters/values
     * @param headers Optional map with headers
     * @return response   Response as string
     */
    static public String postForm(String url, Map<String, String> params,
                                  Map<String, String> headers) throws IOException {
        // set content type
        if (headers == null) {
            headers = new HashMap<String, String>();
        }
        headers.put("Content-Type", "application/x-www-form-urlencoded");

        // parse parameters
        String body = "";
        if (params != null) {
            boolean first = true;
            for (String param : params.keySet()) {
                if (first) {
                    first = false;
                } else {
                    body += "&";
                }
                String value = params.get(param);
                body += URLEncoder.encode(param, "UTF-8") + "=";
                body += URLEncoder.encode(value, "UTF-8");
            }
        }

        return post(url, body, headers);
    }

    /**
     * Send a put request
     *
     * @param url     Url as string
     * @param body    Request body as string
     * @param headers Optional map with headers
     * @return response   Response as string
     */
    static public String put(String url, String body,
                             Map<String, String> headers) throws IOException {
        return fetch("PUT", url, body, headers);
    }

    /**
     * Send a put request
     *
     * @param url Url as string
     * @return response   Response as string
     */
    static public String put(String url, String body) throws IOException {
        return put(url, body, null);
    }

    /**
     * Send a delete request
     *
     * @param url     Url as string
     * @param headers Optional map with headers
     * @return response   Response as string
     */
    static public String delete(String url,
                                Map<String, String> headers) throws IOException {
        return fetch("DELETE", url, null, headers);
    }

    /**
     * Send a delete request
     *
     * @param url Url as string
     * @return response   Response as string
     */
    static public String delete(String url) throws IOException {
        return delete(url, null);
    }

    /**
     * Append query parameters to given url
     *
     * @param url    Url as string
     * @param params Map with query parameters
     * @return url        Url with query parameters appended
     */
    static public String appendQueryParams(String url,
                                           Map<String, String> params) throws IOException {
        String fullUrl = url;
        if (params != null) {
            boolean first = (fullUrl.indexOf('?') == -1);
            for (String param : params.keySet()) {
                if (first) {
                    fullUrl += '?';
                    first = false;
                } else {
                    fullUrl += '&';
                }
                String value = params.get(param);
                fullUrl += URLEncoder.encode(param, "UTF-8") + '=';
                fullUrl += URLEncoder.encode(value, "UTF-8");
            }
        }

        return fullUrl;
    }

    /**
     * Retrieve the query parameters from given url
     *
     * @param url Url containing query parameters
     * @return params     Map with query parameters
     */
    static public Map<String, String> getQueryParams(String url)
            throws IOException {
        Map<String, String> params = new HashMap<String, String>();

        int start = url.indexOf('?');
        while (start != -1) {
            // read parameter name
            int equals = url.indexOf('=', start);
            String param = "";
            if (equals != -1) {
                param = url.substring(start + 1, equals);
            } else {
                param = url.substring(start + 1);
            }

            // read parameter value
            String value = "";
            if (equals != -1) {
                start = url.indexOf('&', equals);
                if (start != -1) {
                    value = url.substring(equals + 1, start);
                } else {
                    value = url.substring(equals + 1);
                }
            }

            params.put(URLDecoder.decode(param, "UTF-8"),
                    URLDecoder.decode(value, "UTF-8"));
        }

        return params;
    }

    /**
     * Returns the url without query parameters
     *
     * @param url Url containing query parameters
     * @return url        Url without query parameters
     */
    static public String removeQueryParams(String url)
            throws IOException {
        int q = url.indexOf('?');
        if (q != -1) {
            return url.substring(0, q);
        } else {
            return url;
        }
    }

    /**
     * Send a request
     *
     * @param method  HTTP method, for example "GET" or "POST"
     * @param url     Url as string
     * @param body    Request body as string
     * @param headers Optional map with headers
     * @return response   Response as string
     */
    static public String fetch(String method, String url, String body,
                               Map<String, String> headers) throws IOException {
        HttpURLConnection conn = getHttpURLConnection(method, url, body, headers);


        // response
        InputStream is = conn.getInputStream();
        String response = streamToString(is);
        is.close();

        // handle redirects
        if (conn.getResponseCode() == 301) {
            String location = conn.getHeaderField("Location");
            return fetch(method, location, body, headers);
        }

        return response;
    }

    public static String getInputToHandler(String url, InvokeHandler<String> invokeHandler) {
        return getInputToHandler("GET", url, null, null, invokeHandler);
    }


    /**
     * 根据地址获得数据的字节流
     *
     * @param strUrl 网络连接地址
     */
    public static byte[] getImageFromNetByUrl(String strUrl) {
        try {
            URL url = new URL(strUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            InputStream inStream = conn.getInputStream();//通过输入流获取图片数据
            byte[] btImg = readInputStream(inStream);//得到图片的二进制数据
            return btImg;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从输入流中获取数据
     *
     * @param inStream 输入流
     */
    private static byte[] readInputStream(InputStream inStream) throws Exception {
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len = inStream.read(buffer)) != -1) {
            outStream.write(buffer, 0, len);
        }
        inStream.close();
        return outStream.toByteArray();
    }

    /**
     * Send a request
     *
     * @param method  HTTP method, for example "GET" or "POST"
     * @param url     Url as string
     * @param body    Request body as string
     * @param headers Optional map with headers
     * @return response   Response as string
     */
    static public String getInputToHandler(String method, String url, String body,
                                           Map<String, String> headers, InvokeHandler<String> invokeHandler) {
        HttpURLConnection conn = null;
        InputStream is = null;
        try {
            conn = getHttpURLConnection(method, url, body, headers);
            // response
            is = conn.getInputStream();
            return invokeHandler.invoke(is);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;

    }


    public interface InvokeHandler<V> {
        V invoke(InputStream inputStream);
    }


    private static HttpURLConnection getHttpURLConnection(String method, String url, String body, Map<String, String> headers) throws IOException {
        // connection
        URL u = new URL(url);

        HttpURLConnection conn = (HttpURLConnection) u.openConnection();
        conn.setConnectTimeout(10000);
        conn.setReadTimeout(10000);

        // method
        if (method != null) {
            conn.setRequestMethod(method);
        }

        // headers
        if (headers != null) {
            for (String key : headers.keySet()) {
                conn.addRequestProperty(key, headers.get(key));
            }
        }

        // body
        if (body != null) {
            conn.setDoOutput(true);
            OutputStream os = conn.getOutputStream();
            os.write(body.getBytes());
            os.flush();
            os.close();
        }
        return conn;
    }

    /**
     * Read an input stream into a string
     *
     * @param in
     */
    static public String streamToString(InputStream in) throws IOException {
        StringBuffer out = new StringBuffer();
        byte[] b = new byte[4096];
        for (int n; (n = in.read(b)) != -1; ) {
            out.append(new String(b, 0, n));
        }
        return out.toString();
    }


    /**
     * 上传文件
     *
     * @param url       文件服务地址
     * @param excelName excel文件名称
     * @param fileName  文件字段名称
     * @param in        文件流
     * @return 文件id
     */
    public static UploadFileResponseBody uploadFile(String url, String excelName, String fileName, InputStream in) {
        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(url);
        MultipartEntityBuilder meb = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);
        ContentType strContent = ContentType.create("application/zip", StandardCharsets.UTF_8);
        meb.addBinaryBody(fileName, in, strContent, excelName);
        meb.setCharset(StandardCharsets.UTF_8);
        HttpEntity httpEntity = meb.build();
        httpPost.setEntity(httpEntity);
        try {
            StringBuilder sb = new StringBuilder();
            String line;
            HttpResponse httpResponse = httpClient.execute(httpPost);
            InputStream inputStream = httpResponse.getEntity().getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            String fileRes = URLDecoder.decode(sb.toString(), "UTF-8");
            return JSON.parseObject(fileRes, UploadFileResponseBody.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 上传下载
     *
     * @param serviceUrl 文件服务地址
     * @param fileId     文件id
     * @param fileName   文件名称
     * @return 文件id
     */
    public static InputStream fileDownload(String serviceUrl, String fileId, String fileName) {
        HttpClient httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(serviceUrl);
        httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
        try {
            JSONObject object = new JSONObject();
            object.put("file_id", fileId);
            object.put("file_name", fileName);
            JSONObject data = new JSONObject();
            data.put("_data_", object);
            StringEntity stringEntity = new StringEntity(data.toJSONString(), "UTF-8");
            httpPost.setEntity(stringEntity);
            HttpResponse httpResponse = httpClient.execute(httpPost);
            if (!Objects.equals(HttpStatus.SC_OK, httpResponse.getStatusLine().getStatusCode())) {
               throw new RuntimeException(httpResponse.getStatusLine().getReasonPhrase());
            }
            return httpResponse.getEntity().getContent();
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("文件下载失败、文件服务地址：{}、文件id：{}、文件名称：{}", serviceUrl, fileId, fileName, e);
            throw new ThinkException("文件下载失败");
        }
    }


}
